#pragma once

#include <utility>
#include <cstddef>

/**
 * @brief 极简版 UniquePtr（独占所有权智能指针）。
 *
 * 设计目标：
 *   - 模仿 std::unique_ptr 的核心语义：独占所有权、移动语义、自定义删除器。
 *   - 便于教学理解，因此实现并未覆盖标准库全部特性（如数组偏特化、面向多态的默认删除器、make_unique 的完备实现等）。
 *
 * 注意事项：
 *   - 该实现仅用于单线程场景。
 *   - 删除器类型默认为 `std::default_delete<T>` 的思想，这里为了减少依赖性直接使用模板参数 Deleter，
 *     默认值为一个简单的调用 `delete` 的函数对象。
 */
template <typename T, typename Deleter = void(*)(T*)>
class UniquePtr {
public:
    /// 删除器默认值：普通函数指针，指向一个调用 delete 的静态函数
    static void default_delete(T* ptr) noexcept {
        delete ptr;
    }

    using pointer      = T*;
    using element_type = T;
    using deleter_type = Deleter;

    /// 默认构造：指针为空，删除器使用默认删除函数
    constexpr UniquePtr() noexcept
        : ptr_(nullptr)
        , deleter_(default_delete) {}

    /// nullptr 构造：等价于默认构造
    constexpr UniquePtr(std::nullptr_t) noexcept
        : UniquePtr() {}

    /**
     * @brief 以裸指针构造 UniquePtr，接管资源所有权。
     * @param ptr    new 出来的对象指针
     */
    explicit UniquePtr(pointer ptr) noexcept
        : ptr_(ptr)
        , deleter_(default_delete) {}

    /**
     * @brief 以裸指针 + 自定义删除器构造。
     * @param ptr      new 出来的对象指针
     * @param deleter  删除器对象（可为函数、函数对象、lambda 等）
     */
    UniquePtr(pointer ptr, deleter_type deleter) noexcept
        : ptr_(ptr)
        , deleter_(std::move(deleter)) {}

    // 禁用拷贝构造与拷贝赋值 —— 独占所有权不允许复制
    UniquePtr(const UniquePtr&)            = delete;
    UniquePtr& operator=(const UniquePtr&) = delete;

    /// 移动构造：接管另一对象的资源，并将其置空
    UniquePtr(UniquePtr&& other) noexcept
        : ptr_(other.ptr_)
        , deleter_(std::move(other.deleter_)) {
        other.ptr_ = nullptr;
    }

    /// 移动赋值：先释放自身已有资源，再接管对方资源
    UniquePtr& operator=(UniquePtr&& other) noexcept {
        if (this != &other) {
            reset();                          // 释放自身资源
            ptr_     = other.ptr_;            // 接管指针
            deleter_ = std::move(other.deleter_);
            other.ptr_ = nullptr;             // 令被移动对象置空
        }
        return *this;
    }

    /// 析构：若持有资源则调用删除器释放
    ~UniquePtr() {
        reset();
    }

    /// 释放当前资源并接管新指针（删除器不变）
    void reset(pointer ptr = nullptr) noexcept {
        if (ptr_ != nullptr) {
            deleter_(ptr_);
        }
        ptr_ = ptr;
    }

    /// 释放当前资源，并同时更新删除器
    void reset(pointer ptr, deleter_type deleter) noexcept {
        reset(ptr);                 // 先释放并接管新指针
        deleter_ = std::move(deleter);
    }

    /// 释放所有权但不删除对象，返回裸指针，调用者需手动 delete
    pointer release() noexcept {
        pointer raw = ptr_;
        ptr_ = nullptr;
        return raw;
    }

    /// 交换两个 UniquePtr 的资源与删除器
    void swap(UniquePtr& other) noexcept {
        std::swap(ptr_, other.ptr_);
        std::swap(deleter_, other.deleter_);
    }

    /// 解引用操作（需确保指针非空）
    element_type& operator*() const noexcept {
        return *ptr_;
    }

    /// 成员访问操作（需确保指针非空）
    pointer operator->() const noexcept {
        return ptr_;
    }

    /// 访问裸指针
    pointer get() const noexcept {
        return ptr_;
    }

    /// 判断是否持有资源
    explicit operator bool() const noexcept {
        return ptr_ != nullptr;
    }

    /// 获取删除器（按引用返回，以便调用者查看或调用）
    deleter_type& get_deleter() noexcept {
        return deleter_;
    }

    const deleter_type& get_deleter() const noexcept {
        return deleter_;
    }

private:
    pointer      ptr_{nullptr};            ///< 当前托管的裸指针
    deleter_type deleter_{default_delete}; ///< 删除器对象
};

/**
 * @brief make_unique 的简单实现：完美转发构造参数并返回 UniquePtr。
 *
 * @tparam T    目标类型
 * @tparam Args 构造参数类型
 * @param args  构造参数
 */
template <typename T, typename... Args>
UniquePtr<T> make_unique(Args&&... args) {
    return UniquePtr<T>(new T(std::forward<Args>(args)...));
}